package uk.co.hephistocles.blobGame;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Vector;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.NinePatch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.actors.Image;
import com.badlogic.gdx.scenes.scene2d.actors.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Button.ClickListener;
import com.ice.tar.InvalidHeaderException;
import com.ice.tar.TarArchive;

public class Game implements ApplicationListener{
	
	//CONSTANTS
	static final boolean debug = false;
	public static boolean touchControl;
	public static boolean keyBoardControl;
	static final int fps = 60;
	private static final int MAINMENU = 0;
	static final int LEVELMENU = 1;
	static final int RUNNING = 2;
	private static final int PAUSED = 3;
	private static final int DIALOG = 4;
	private static final int levelsNum = 15;
	public static FileHandle INTERNAL;
	public static FileHandle EXTERNAL;
	
	//LEVEL-related stuff
	public static int		ACTIVEWORLD =1;
	public static int		TOTWORLDS;
    private long 			lastTime;
    static Level			level;
	public 	static int				currLevel;
	
	//offX and offY represent the camera offset
	private  double			offX = 0;
	private  double			offY = 0;
	
	//MENU-related stuff
	private Dialog					menu;
	private LevelMenu				levelMenu;
	public static Texture			starsTex;
	public static  Texture			btnCross;
	public static Image				menuBg;
	public static  TextureRegion[] 	stars;
	public static  NinePatch		upBtn;
	public static  NinePatch		dwnBtn;
	public static BitmapFont		fontS;
	public static BitmapFont		fontM;
	public static BitmapFont		fontL;
	public static BitmapFont		btnFont;
	
	//OTHER
	public	int				state;
	private long			pausedTime;
	
	//DEBUG STUFF
	private static int frameCount;
	private static float runTime;
	private Pixmap			pixmap;
    private Texture			texture;
    private SpriteBatch 	batch;
    private TextureRegion 	region;
	
    private IActivityRequestHandler myRequestHandler;

    public Game(IActivityRequestHandler handler) {
        myRequestHandler = handler;
    }
	@Override
	public void create() {
		setBGColor();
		INTERNAL = Gdx.files.internal("");
		EXTERNAL = Gdx.files.absolute(Gdx.files.getExternalStoragePath() + "/Android/data/uk.co.hephistocles.blobGame/files/");
		
		
		if (!EXTERNAL.exists()) {
			EXTERNAL.mkdirs();
			TarArchive tar;
			tar = new TarArchive(INTERNAL.child("worlds/1.tar").read());
			try {
				tar.extractContents(new File( EXTERNAL.path()+"/"));
			} catch (InvalidHeaderException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("Copied files!");
		}
		
		if (Gdx.app.getVersion()>0) {
			keyBoardControl = false;
			touchControl = false;
		} else {
			keyBoardControl = true;
			touchControl = true;			
		}
		
		//RemoteInput receiver = new RemoteInput();
		//Gdx.input = receiver;

		//deliberately not disposed until gamedie
		fontL = new BitmapFont(Gdx.files.internal("res/large.fnt"), false);
		fontM = new BitmapFont(Gdx.files.internal("res/med.fnt"), false);
		fontS = new BitmapFont(Gdx.files.internal("res/small.fnt"), false);
		btnFont = new BitmapFont(Gdx.files.internal("res/btn.fnt"),false);
		
        Texture upTex = new Texture(Gdx.files.internal("res/upbtn.png"));
		upBtn = new NinePatch(upTex, 25,64-39,24,64-43);

	    Texture dwnTex = new Texture(Gdx.files.internal("res/dwnbtn.png"));
		dwnBtn = new NinePatch(dwnTex, 25,64-39,24,64-43);
		
		btnCross = new Texture(Gdx.files.internal("res/cross.png"));
		starsTex = new Texture(Gdx.files.internal("res/stars.png"));
		
		menuBg = new Image("menuBg",new Texture (Gdx.files.internal("res/menubg.png")));
		menuBg.x = (Gdx.graphics.getWidth()-menuBg.width)/2;
		menuBg.y = (Gdx.graphics.getHeight()-menuBg.height)/2;
		
		stars = new TextureRegion[4];
		stars[0] = new TextureRegion(starsTex,0,0,32,32);
		stars[1] = new TextureRegion(starsTex,0,32,32,32);
		stars[2] = new TextureRegion(starsTex,0,64,32,32);
		stars[3] = new TextureRegion(starsTex,0,96,64,32);
		
		menu = new Dialog();
		Gdx.input.setCatchBackKey(true);
		Gdx.input.setCatchMenuKey(true); 
		levelMenu = new LevelMenu();
		
		level = new Level(this);
		countWorlds();
		makeMainMenu();
		lastTime = System.currentTimeMillis();
		pausedTime = System.currentTimeMillis();
		if (Game.debug) {
			batch = new SpriteBatch();
		}
	}
	
	private void setBGColor() {
        Gdx.gl.glClearColor(0,0,0,1); 
	}
	
	static void startTrace() {

		//Debug.startMethodTracing("level");
		frameCount = 0;
		runTime = 0;
	}
	private void stopTrace() {
		//Debug.stopMethodTracing();
		System.out.println("FRAMES:"+frameCount+" TIME: "+runTime+" AVERAGE FPS: "+(frameCount/runTime));
		HashMap<String, String> event = new HashMap<String,String>();
		event.put("FRAMES", ""+frameCount);
		event.put("TIME", "" +runTime);
		event.put("AVERAGE FPS", ""+(frameCount/runTime));
		myRequestHandler.addEvent("FPS Counted", event);
	}
	
	private void makeMainMenu() {
		Gdx.input.setInputProcessor(menu);
		lastTime = System.currentTimeMillis();
		menu.addImage(menuBg);
		Button play = menu.addButton("gameBtn", "play", btnFont, dwnBtn, upBtn, new ClickListener(){

			@Override
			public void click(Button button) {
				menu.clear();
				HashMap<String, String> event = new HashMap<String,String>();
				event.put("testParam","true");
				myRequestHandler.addEvent("ENTERGAME",event);
				makeLevelMenu();
			}});
		play.x = Gdx.graphics.getWidth()/2-play.width/2;
		play.y = Gdx.graphics.getHeight()/2-(0.5f*play.height);

		/*Button getPapaya = menu.addButton("gameBtn", "pp", btnFont, dwnBtn, upBtn, new ClickListener(){

			@Override
			public void click(Button button) {

				HashMap<String, String> event = new HashMap<String,String>();
				event.put("TYPE", "Papaya");
				myRequestHandler.addEvent("Showed Ads", event);
				myRequestHandler.showAds("Papaya");
			}});
		getPapaya.x = Gdx.graphics.getWidth()/2-3*getPapaya.width/2;
		getPapaya.y = Gdx.graphics.getHeight()/2-(1.5f*getPapaya.height);
		
		Button getLeadBolt = menu.addButton("gameBtn", "lb", btnFont, dwnBtn, upBtn, new ClickListener(){

			@Override
			public void click(Button button) {
				HashMap<String, String> event = new HashMap<String,String>();
				event.put("TYPE", "Leadbolt");
				myRequestHandler.addEvent("Showed Ads", event);
				myRequestHandler.showAds("Leadbolt");
			}});
		getLeadBolt.x = Gdx.graphics.getWidth()/2-getLeadBolt.width/2;
		getLeadBolt.y = Gdx.graphics.getHeight()/2-(1.5f*getLeadBolt.height);
		
		Button getFlurry = menu.addButton("gameBtn", "ac", btnFont, dwnBtn, upBtn, new ClickListener(){

			@Override
			public void click(Button button) {
				HashMap<String, String> event = new HashMap<String,String>();
				event.put("TYPE", "AppCircle");
				myRequestHandler.addEvent("Showed Ads", event);
				myRequestHandler.showAds("AppCircle");
			}});
		getFlurry.x = Gdx.graphics.getWidth()/2+getFlurry.width/2;
		getFlurry.y = Gdx.graphics.getHeight()/2-(1.5f*getFlurry.height);
		*/

		Button clearScore = menu.addButton("gameBtn", "clear score", btnFont, dwnBtn, upBtn, new ClickListener(){

			@Override
			public void click(Button button) {
				//Update level locks
				FileHandle handle = Game.EXTERNAL.child("1/scores.txt");
				
				PrintWriter levelTxt = new PrintWriter(handle.write(false));
				
				levelTxt.print("1,0,0,0,0,0;");
				for (int i=1; i<15; i++) {
					levelTxt.print("0,0,0,0,0,0;");
				}
				levelTxt.close();
			}});
		Button settings = menu.addButton("gameBtn", "Do Nothing", btnFont, dwnBtn, upBtn, new ClickListener(){

			@Override
			public void click(Button button) {
			}});
		settings.x = Gdx.graphics.getWidth()/2-settings.width/2;
		settings.y = Gdx.graphics.getHeight()/2-(1.5f*settings.height);
		
		clearScore.x = Gdx.graphics.getWidth()/2-clearScore.width/2;
		clearScore.y = Gdx.graphics.getHeight()/2+(0.5f*clearScore.height);
		state = MAINMENU;
	}
	
	private int countWorlds() {
		TOTWORLDS = EXTERNAL.child("auth").readString().split(",").length/2;
		return TOTWORLDS;
	}
	
	void makeLevelMenu() {
		Gdx.input.setInputProcessor(levelMenu);
		lastTime = System.currentTimeMillis();
		levelMenu.clear();
		levelMenu.prepareWorld(ACTIVEWORLD);
		
				
		state = LEVELMENU;
	}

	
	@Override
	public void resume() {
		setBGColor();
	}

	@Override
	public void render() {
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // This cryptic line clears the screen.
        runTime += Gdx.graphics.getDeltaTime();
        boolean back = false;
		if (Gdx.input.isKeyPressed(Input.Keys.BACK)) {
			back = true;
		}
        
        if (state == MAINMENU) {
        	menu.update(Gdx.graphics.getDeltaTime());
        	menu.draw();
        	if (back){
				if (System.currentTimeMillis() - lastTime>500) {
					dispose();
					System.out.println("Returned home from main");
					myRequestHandler.finish();
					
				}
        	}
        } else if (state == LEVELMENU) {
        	levelMenu.update(Gdx.graphics.getDeltaTime());
        	levelMenu.draw();

        	if (back){
				if (System.currentTimeMillis() - lastTime>500) {
					levelMenu.clear();
					System.out.println("Returned to main from level");
					makeMainMenu();
				}
        	}
        } else if (state == RUNNING) {

	    	//only perform calculations and update display after sufficient time has elapsed
			if (System.currentTimeMillis() - lastTime>1000/fps) {
		        level.update((int) (1000/fps));
		        frameCount++;
		        lastTime = System.currentTimeMillis();
			}
			if (System.currentTimeMillis() - pausedTime>500) {
				if ((!touchControl) &&(Gdx.input.isTouched())) {
					pauseGame();
				} else if (Gdx.input.isKeyPressed(Input.Keys.MENU)) {
					pauseGame();
				}
				
				if (back) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
			    	level.dispose();
					lastTime = System.currentTimeMillis();
					System.out.println("Returned to level from game");
					stopTrace();
					makeLevelMenu();
	        	}
			}
			level.draw();
        } else if (state == PAUSED) {
			if (System.currentTimeMillis() - pausedTime>500) {
				if (Gdx.input.isTouched() || Gdx.input.isKeyPressed(Input.Keys.MENU) || back) {
					unpauseGame();
				}
			}
			level.draw();
        	menu.update(Gdx.graphics.getDeltaTime());
        	menu.draw();
        } else if (state == DIALOG) {
        	level.draw();
        	menu.update(Gdx.graphics.getDeltaTime());
        	menu.draw();
        	
			if (back) {
				menu.clear();
		    	pausedTime = System.currentTimeMillis();
		    	level.dispose();
				lastTime = System.currentTimeMillis();
				stopTrace();
				makeLevelMenu();
        	}
        }
	}
	
	private void pauseGame() {
		pausedTime = System.currentTimeMillis();
		state = PAUSED;
		Label tmp = menu.addLabel("pauseText", "This game is paused. Click anywhere to unpause", 
				fontM, Color.BLACK);
		tmp.x = (Gdx.graphics.getWidth() - tmp.width)/2;
		tmp.y = (Gdx.graphics.getHeight())/2;
	}
	
	private void unpauseGame() {
    	pausedTime = System.currentTimeMillis();
		state = RUNNING;
		menu.clear();
	}
	
	void die(HashMap<String,String> eventParams) {
		Gdx.input.setInputProcessor(menu);
		stopTrace();
		myRequestHandler.addEvent("Die", eventParams);
		pausedTime = System.currentTimeMillis();
		state = DIALOG;
		Vector<Button> tmp = new Vector<Button>();
		tmp.add(menu.addButton("restartBtn", "R", btnFont,
				dwnBtn,	upBtn, new ClickListener() {
				@Override
				public void click(Button btn) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
					state = RUNNING;
					level.initLevel(ACTIVEWORLD, currLevel);

					HashMap<String, String> event = new HashMap<String,String>();
					event.put("Action", "Restart");
					myRequestHandler.addEvent("DeathAction", event);
					startTrace();
				}}));
		tmp.add(menu.addButton("mainBtn", "M", btnFont,
				dwnBtn,	upBtn, new ClickListener() {
				@Override
				public void click(Button btn) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
			    	level.dispose();
					lastTime = System.currentTimeMillis();
					HashMap<String, String> event = new HashMap<String,String>();
					event.put("Action", "Main Menu");
					myRequestHandler.addEvent("DeathAction", event);
					makeMainMenu();
				}}));
		tmp.add(menu.addButton("levelBtn", "L", btnFont,
				dwnBtn,	upBtn, new ClickListener() {
				@Override
				public void click(Button btn) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
			    	level.dispose();
					lastTime = System.currentTimeMillis();
					HashMap<String, String> event = new HashMap<String,String>();
					event.put("Action", "Level Menu");
					myRequestHandler.addEvent("DeathAction", event);
					makeLevelMenu();
				}}));
		float wid = Gdx.graphics.getWidth()/tmp.size();
		for (int i=0; i<tmp.size(); i++) {
			tmp.get(i).y = Gdx.graphics.getHeight()/4;
			tmp.get(i).x = i*wid + wid/2 - tmp.get(i).width/2;
		}
		Label lbl = menu.addLabel("pauseText", "Whoops, you died. Nice.", 
				fontM, Color.BLACK);
		lbl.y = Gdx.graphics.getHeight()*3/4;
		lbl.x = (Gdx.graphics.getWidth() - lbl.width)/2;
	}
	
	void win(boolean blueStar, boolean greenStar, boolean goldStar, int score, HashMap<String, String> eventParams) {
		stopTrace();
		float highScore = 0;
		//Update level locks
		FileHandle handle = EXTERNAL.child(ACTIVEWORLD+"/scores.txt");
		
		if (handle.exists()) {
			String[] levels = handle.readString().split(";");
			PrintWriter levelTxt = new PrintWriter(handle.write(false));
			
			for (int i=0; i<levels.length; i++) {
				String[] levelData = levels[i].split(",");
				if (i == currLevel-1) {
					String level = levelData[0]+",";
					level += greenStar?"1,":levelData[1] + ",";
					level += goldStar?"1,": levelData[2]+",";
					level += blueStar?"1,": levelData[3]+",";
					level += (blueStar&&goldStar&&greenStar)?"1,": levelData[4]+",";
					highScore = Float.parseFloat(levelData[5]);
					eventParams.put("High Score", levelData[5]);
					level += Integer.toString((int)Math.max(score, Float.parseFloat(levelData[5])))+";";
					levelTxt.print(level);
				} else if (i == currLevel) {
					String level = "1,";
					level += levelData[1] + ",";
					level += levelData[2] + ",";
					level += levelData[3] + ",";
					level += levelData[4] + ",";
					level += levelData[5] +";";
					levelTxt.print(level);
				} else {
					String level = levels[i]+";";
					levelTxt.print(level);
				}
			}
			levelTxt.close();
		} else {
			throw new Error("No level data found. Sorry =(");
		}
		myRequestHandler.addEvent("Win", eventParams);
		

		pausedTime = System.currentTimeMillis();
		state = DIALOG;

		Gdx.input.setInputProcessor(menu);
		Vector<Button> tmp = new Vector<Button>();
		tmp.add(menu.addButton("nxtBtn", "N", btnFont,
				dwnBtn,	upBtn, new ClickListener() {
				@Override
				public void click(Button btn) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
					state = RUNNING;
					level.initLevel(ACTIVEWORLD, currLevel+1);

					HashMap<String, String> event = new HashMap<String,String>();
					event.put("Action", "Next Level");
					myRequestHandler.addEvent("WinAction", event);
					startTrace();
				}}));
		tmp.add( menu.addButton("restartBtn", "R", btnFont,
				dwnBtn,	upBtn, new ClickListener() {
				@Override
				public void click(Button btn) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
					state = RUNNING;
					level.initLevel(ACTIVEWORLD, currLevel);

					HashMap<String, String> event = new HashMap<String,String>();
					event.put("Action", "Retry");
					myRequestHandler.addEvent("WinAction", event);
					startTrace();
				}}));
		tmp.add(menu.addButton("levelBtn", "L", btnFont,
				dwnBtn,	upBtn, new ClickListener() {
				@Override
				public void click(Button btn) {
					menu.clear();
			    	pausedTime = System.currentTimeMillis();
			    	level.dispose();
					lastTime = System.currentTimeMillis();


					HashMap<String, String> event = new HashMap<String,String>();
					event.put("Action", "Level Menu");
					myRequestHandler.addEvent("WinAction", event);
					makeLevelMenu();
				}}));
		float wid = Gdx.graphics.getWidth()/tmp.size();
		for (int i=0; i<tmp.size(); i++) {
			tmp.get(i).y = Gdx.graphics.getHeight()/4;
			tmp.get(i).x = i*wid + wid/2 - tmp.get(i).width/2;
		}
		if (currLevel > 14) {
			tmp.get(0).touchable = false;
			tmp.get(0).visible = false;
		}
		
		Label lbl = menu.addLabel("pauseText", "", 
				fontM, Color.BLACK);
		lbl.setMultiLineText("Wehay! You won!\n" + ((highScore<score)?" (New High Score!)":"(High Score: "+(int)(highScore)+")"));
		lbl.y = Gdx.graphics.getHeight()*3/4;
		lbl.x = (Gdx.graphics.getWidth() - lbl.width)/2;
		if (blueStar) {
			menu.addAnimImage("blueStar", new Texture(Gdx.files.internal("res/bluestar.png")),
					100,100, 28, 56, 21, 7).random();
		}
		if (greenStar) {
			menu.addAnimImage("greenStar", new Texture(Gdx.files.internal("res/greenstar.png")),
					200,100, 28, 56, 21, 7).random();
		}
		if (goldStar) {
			menu.addAnimImage("goldStar", new Texture(Gdx.files.internal("res/goldstar.png")),
					300,100, 28, 56, 21, 7).random();
		}
	}
	
	@Override
	public void resize(int width, int height) {
	}

	@Override
	public void pause() {
	}

	@Override
	public void dispose() {
		menu.clear();
		menu.dispose();
		fontL.dispose();
		fontM.dispose();
		fontS.dispose();
		btnFont.dispose();
		level.dispose();
		btnCross.dispose();
		starsTex.dispose();
	}

}
